﻿// Inner Fire 游戏引擎库
// DirectX 渲染器主类
//
// Copyright (c) 2024 by 尔西大帝. All rights reserved.
//
// 用于DirectX的渲染器
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-11-30

#include "dx_graphics.h"
#include <DDSTextureLoader.h>
#include <d3dcompiler.h>
#include <dx/mesh/dx_mesh_factory.h>
#include <functional>
#include <game/core/timer.h>
#include <graphics/imath.h>
#include <kfile.h>
#include <klog.h>
#include <stop_watch.h>

namespace ifire::dx {

// 默认的平行光
Light directional_light = {};

#pragma region Init

DxGraphics::DxGraphics()
    : view_(this)
    , draw_(this)
    , initer_(this)
    , swapchain_(this)
    , post_processing_(this)
    , lights_(this)
    , shadows_(this)
    , pass_(this)
    , pipeline_(this)
    , frame_res_(this)
    , ui_(this)
    , instanced_sprite_(this)
    , rootsign_(this) {}

DxGraphics::~DxGraphics() {
  if (Device() != nullptr) {
    FlushCommandQueue();
  }
}

// ------
// 这两个函数，一个InitPipeline，一个Update，放在放前面，因为会经常看到
// ------
void DxGraphics::InitPipeline() {
  // 重置命令列表，为执行初始化命令做好准备工作
  ResetCommandList();

  // ------
  // 初始化默认的Mesh
  // 这本来应该是一个Singleton模式的，但因为不知道为个DxMesh的释放总是出问题。
  // 所以不得已把这个东西的实例放到这个DxGraphics中来了。
  // ------
  mesh_factory_ = std::make_unique<DxMeshFactory>();
  mesh_factory_->CreateSystemMesh(Device(), CommandList());
  DxMeshFactory::SetInstance(mesh_factory_.get());

  // ------
  // 初始化RenderTexture
  // ------

  // 阴影贴图
  shadows_.Init();
  // 用于GPU波浪
  waves_ = std::make_unique<GpuWaves>(
      Device(), CommandList(), 256, 256, 0.25f, 0.05f, 2.0f, 0.2f);

  // 初始化后处理
  post_processing_.Init();
  // 初始化UI
  ui_.Init();

  // 这里经过测试BuildShader是需要1秒，然后这个函数的其它部分需要1秒。
  pipeline_.BuildShaderAndInputLayout(GetDebugDefines());
  rootsign_.BuildRootSignature();
  frame_res_.BuildFrameResources();

  // ------
  // 初始化PSO
  // ------
  pipeline_.BuildPipelineStates();
  // 设置Ssao的项目，如果设置为Enable，则在一开始就会创建
  if (post_processing_.GetSsaoEnable()) {
    post_processing_.GetSsao()->SetPSOs(
        pipeline_["Ssao"], pipeline_["SsaoBlur"]);
  }

  ExecuteCommands();
  FlushCommandQueue();

  is_inited_ = true;

  // 更新一次光源，保证光源的信息都被初始化了。
  lights_.UpdateLights();
}

void DxGraphics::Update() {
  // 更新相机
  view_.UpdateCamera();

  // 切换帧资源
  frame_res_.SwitchFrameResource();

  // 更新对象
  auto obj_cb = CurrentFrameRes()->object_cb.get();
  for (auto& e : Items()) {
    e->Update(obj_cb);
  }

  // 更新蒙皮骨骼对象
  auto skinned_cb = CurrentFrameRes()->skinned_cb.get();
  for (auto& e : Animators()) {
    e->Update(skinned_cb);
  }

  // 更新UI
  auto text_cb = CurrentFrameRes()->text_cb.get();
  ui_.GetMesh()->UpdateText(text_cb);
  auto image_cb = CurrentFrameRes()->ui_cb.get();
  ui_.GetMesh()->UpdateImage(image_cb);

  // 更新材质
  auto material_buffer = CurrentFrameRes()->material_buffer.get();
  for (auto& e : Materials()) {
    e->Update(material_buffer);
  }

  // 更新阴影
  shadows_.UpdateShadowTransform();

  // 更新主PASS，这个要放在前面，因为之后有些Pass需要MainPass内的成员
  pass_.UpdateMain();
  shadows_.UpdateShadowPass();
  pass_.UpdateSsao();

  // 更新实例精灵
  instanced_sprite_.Update();
}

void DxGraphics::FixedUpdate() {
  // 既然相机的改动才导致神椎体的改动，那么，在相机发生变化之后才计算视锥体剔除不更好？
  // 至少是在FixedUpdate中，可以节约一些算力。
  view_.OnCameraInput();
  view_.PerformFrustumCulling();
  // 旋转光源
  lights_.RotateLights();
  // 水体的纹理变动
  // AnimateMaterials();
}

void DxGraphics::UpdateSecond() {
  // 更新光源配置
  // UpdateLights();
}

void DxGraphics::Set4xMsaaState(bool value) {
  if (DxUtils::Get4xMsaaState() != value) {
    DxUtils::Set4xMsaaState(value);

    // Recreate the swapchain and buffers with new multisample settings.
    swapchain_.CreateSwapChain();
    OnResize();
  }
}

void DxGraphics::ExecuteCommands() {
  HASSERT(CommandList()->Close());
  ID3D12CommandList* cmd_lists[] = {CommandList()};
  GetCmdQueue()->ExecuteCommandLists(_countof(cmd_lists), cmd_lists);
}

ComPtr<ID3D12Resource> DxGraphics::CreateDefaultBuffer(
    const void* data, UINT64 data_size, ComPtr<ID3D12Resource>& upload_buffer) {
  return DxUtils::CreateDefaultBuffer(
      Device(), CommandList(), data, data_size, upload_buffer);
}

HRESULT DxGraphics::LoadDDS(_In_z_ const char* szFileName,
    _Out_ ComPtr<ID3D12Resource>& texture,
    _Out_ ComPtr<ID3D12Resource>& textureUploadHeap) {
  return DirectX::CreateDDSTextureFromFile12(Device(), CommandList(),
      ikit::to_wide(szFileName).c_str(), texture, textureUploadHeap);
}

size_t DxGraphics::PrintStatus(const char* format, ...) {
  va_list args;
  va_start(args, format);
  size_t result = vsnprintf_s(mStatus, sizeof(mStatus), format, args);
  va_end(args);
  return result;
}

void DxGraphics::ResetCommandList() {
  HASSERT(CommandList()->Reset(GetAllocator(), nullptr));
}

void DxGraphics::LoadConfig(const std::string filename) {
  config.Load(filename.c_str());

  // 设置配置信息
  view_.InitConfig();
  post_processing_.InitConfig();
  shadows_.InitConfig();
  pass_.InitConfig();
  frame_res_.InitConfig();

  // 各种状态的开关
  show_debug_layer_ = ReadDebugLayer(config("ShowDebugLayer"));
}

void DxGraphics::InitDirect3D(HWND hwnd, UINT width, UINT height) {
  hwnd_ = hwnd;
  SetClientSize(width, height);

  EnableDebugLayer();
  initer_.CreateDevice();
  DxUtils::CheckMsaa(Device());
  initer_.CreateCommandObjects();
  swapchain_.CreateSwapChain();
  descriptor_.Init(Device());
}

void DxGraphics::EnableDebugLayer() {
  // 启用调式层
#if defined(DEBUG) || defined(_DEBUG)
  ComPtr<ID3D12Debug> debugController;
  HASSERT(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)));
  debugController->EnableDebugLayer();
#endif
}

void DxGraphics::OnResize() {
  assert(Device());

  // 在进行资源更改之前，需要将队列清空
  FlushCommandQueue();

  // 重置命令队列
  ResetCommandList();

  // 清空SwapChain，以进行重新创建
  swapchain_.Reset();
  // 为每一个SwapChain创建RTV视图
  swapchain_.CreateRenderTargetView();
  // 创建DSV视图
  swapchain_.CreateDepthStencilView();

  // 执行Resize命令
  ExecuteCommands();

  // 等待命令执行完成
  FlushCommandQueue();

  // 更新视口与剪裁
  view_.UpdateViewport();

  // 更新UI的项目
  ui_.OnResize();

  // 更新相机状态
  view_.ResetCamera();

  // 更新离屏纹理
  post_processing_.Reset();
}

ID3D12Resource* DxGraphics::GetBackBuffer() const {
  return swapchain_.GetBackBuffer();
}

D3D12_CPU_DESCRIPTOR_HANDLE DxGraphics::GetBackBufferView() const {
  return swapchain_.GetBackBufferView();
}

D3D12_CPU_DESCRIPTOR_HANDLE DxGraphics::GetDepthStencilView() const {
  return descriptor_.dsv_heap_->GetCPUDescriptorHandleForHeapStart();
}
#pragma endregion

#pragma region Pipeline

// 设置预制值
const static D3D_SHADER_MACRO shadow_debug[] = {
    "SHADOW_DEBUG", "1", NULL, NULL};
const static D3D_SHADER_MACRO ssao_debug[] = {"SSAO_DEBUG", "1", NULL, NULL};
const D3D_SHADER_MACRO* DxGraphics::GetDebugDefines() {
  if (show_debug_layer_ == DebugLayer::Shadow) {
    return shadow_debug;
  } else if (show_debug_layer_ == DebugLayer::Ssao) {
    return ssao_debug;
  }
  return nullptr;
}

DxText* DxGraphics::CreateText(const DxTextDesc& desc, UIRenderItem* parent) {
  return ui_.GetMesh()->CreateText(desc, parent);
}

DxImage* DxGraphics::CreateImage(
    const DxImageDesc& desc, UIRenderItem* parent) {
  return ui_.GetMesh()->CreateImage(desc, parent);
}

DxLayout* DxGraphics::CreateLayout(
    const DxLayoutDesc& desc, UIRenderItem* parent) {
  return ui_.GetMesh()->CreateLayout(desc, parent);
}

void DxGraphics::SetTextureDescriptor(DxTexture* texture) {
  if (texture->Resource() != nullptr) {
    // 创建一个Texture的描述符堆，并且返回Offset的索引
    auto offset_index =
        descriptor_.CreateTexture(texture->Type(), texture->Resource());
    // 设置索引到Texture
    texture->SetIndex(offset_index);
  } else {
    KERROR("Texture resource is empty: %s", texture->Name().c_str());
  }
}

void DxGraphics::SetDrawDesc(const DxDrawDesc& desc) {
  draw_.SetDesc(desc);
  descriptor_.AssignSkyBox(desc.sky_box->Resource());
  descriptor_.AssignEmptyNormalMap(desc.empty_normal->Resource());
}

void DxGraphics::BuildDescriptorHeaps() {
  // 快捷访问（用于易读性）
  auto& des = descriptor_;

  // 初始化Descriptor堆
  DxDescriptorDesc desc = {};
  desc.TextureCount = 1024;
  desc.WavesCount = waves_->DescriptorCount();
  desc.SobelCount = post_processing_.GetSobel()->DescriptorCount();
  des.BuildSrv(desc);

  // 创建阴影贴图
  shadows_.GetShadowMap()->BuildDescriptors(
      des.GetCpuHandle(des.mShadowMapIndex),
      des.GetGpuHandle(des.mShadowMapIndex), des.GetDsv(SHADOW_DSV_INDEX));

  // 创建GPUWaves
  waves_->BuildDescriptors(des.GetCpuHandle(des.mWavesIndex),
      des.GetGpuHandle(des.mWavesIndex), des.CBV_SRV_UAV_SIZE);

  // 创建Ssao贴图
  post_processing_.GetSsao()->BuildDescriptors(swapchain_.GetDepthStencil(),
      des.GetCpuHandle(des.mSsaoIndex), des.GetGpuHandle(des.mSsaoIndex),
      des.GetRtv(SWAP_CHAIN_BUFFER_COUNT), des.CBV_SRV_UAV_SIZE, des.RTV_SIZE);
  // 创建索贝尔算子
  post_processing_.GetSobel()->BuildDescriptors(
      des.GetCpuHandle(des.mSobelIndex), des.GetGpuHandle(des.mSobelIndex),
      des.CBV_SRV_UAV_SIZE);
  // 创建屏幕后处理
  post_processing_.GetOffscreenRt()->BuildDescriptors(
      des.GetCpuHandle(des.mOffscreenIndex),
      des.GetGpuHandle(des.mOffscreenIndex),
      des.GetRtv(des.mRtvOffscreenIndex));
  // 创建UI深度图
  ui_.GetDepthMap()->BuildDescriptors(des.GetCpuHandle(des.mUIDepthMapIndex),
      des.GetGpuHandle(des.mUIDepthMapIndex), des.GetDsv(UI_DSV_INDEX));
}

#pragma endregion

#pragma region Update

void DxGraphics::OnMouseDown(WPARAM btn_state, int x, int y) {
  last_mouse_pos_.x = x;
  last_mouse_pos_.y = y;

  if (view_.GetCameraType() == CameraType::SPHERICAL) {
    SetCapture(hwnd_);
  }
}

void DxGraphics::OnMouseUp(WPARAM btn_state, int x, int y) {
  if (view_.GetCameraType() == CameraType::SPHERICAL) {
    ReleaseCapture();
  }
}

void DxGraphics::OnMouseMove(WPARAM btn_state, int x, int y) {
  // 得到位移
  float move_x = static_cast<float>(x - last_mouse_pos_.x);
  float move_y = static_cast<float>(y - last_mouse_pos_.y);

  // 处理相机
  view_.OnMouseMove(btn_state, move_x, move_y);

  // 记录最后
  last_mouse_pos_.x = x;
  last_mouse_pos_.y = y;
}

void DxGraphics::OnMouseWheel(int delta) { view_.OnMouseWheel(delta); }

#pragma endregion

} // namespace ifire::dx
